﻿//-------------------------------------------------------------------
//	Copyright (c) 2010-2014  Zhirnov Andrey
//	This file is part of the "UX Standard Lib" project.
//	See copyright notice in "UX_STL_Core.h".
//-------------------------------------------------------------------


	//
	// Vector
	//

	template <typename T>
	struct TVec<T,I>
	{
	// types
		typedef bool			_is_vector;
		typedef TVec<T,I>		Self;
		typedef T				value_t;
		typedef TVec<bool,I>	bvec;
		typedef T				arr_t[I];
		enum { STATIC_COUNT = I };


#	if I == 1
	// variables
		T	x;

	// constructors
		TVec(): x(0) {}
		TVec(const TVec<T,1> &v): x(v.x) {}

		explicit TVec(T X): x(X) {}
		explicit TVec(const TVec<T,2> &v): x(v.x) {}
		explicit TVec(const TVec<T,3> &v): x(v.x) {}
		explicit TVec(const TVec<T,4> &v): x(v.x) {}

		template <typename B>
		explicit TVec(const B *p)	{ ASSUME( p != null );  x = T(p[0]); }

		template <typename B>
		explicit TVec(const TVec<B,1> &v): x(T(v.x)) {}
#	endif

#	if I == 2
	// variables
		T	x,
			y;

	// constructors
		TVec(): x(0), y(0) {}
		TVec(const TVec<T,2> &v): x(v.x), y(v.y) {}

		explicit TVec(T V): x(V), y(V) {}
		explicit TVec(T X, T Y): x(X), y(Y) {}
		explicit TVec(const TVec<T,3> &v): x(v.x), y(v.y) {}
		explicit TVec(const TVec<T,4> &v): x(v.x), y(v.y) {}

		template <typename B>
		explicit TVec(const B *p)	{ ASSUME( p != null );  x = T(p[0]);  y = T(p[1]); }

		template <typename B>
		explicit TVec(const TVec<B,2> &v): x(T(v.x)), y(T(v.y)) {}
#	endif

#	if I == 3
	// variables
		T	x,
			y,
			z;

	// constructors
		TVec(): x(0), y(0), z(0) {}
		TVec(const TVec<T,3> &v): x(v.x), y(v.y), z(v.z) {}

		explicit TVec(T V): x(V), y(V), z(V) {}
		explicit TVec(T X, T Y, T Z): x(X), y(Y), z(Z) {}

		template <typename A>
		explicit TVec(A X, const TVec<T,2> &YZ): x(X), y(YZ[0]), z(YZ[1]) {}

		template <typename A>
		explicit TVec(const TVec<T,2> &XY, A Z): x(XY[0]), y(XY[1]), z(Z) {}

		explicit TVec(const TVec<T,2> &XY): x(XY[0]), y(XY[1]), z(0) {}
		explicit TVec(const TVec<T,4> &v): x(v.x), y(v.y), z(v.z) {}

		template <typename B>
		explicit TVec(const B *p)	{ ASSUME( p != null );  x = T(p[0]);  y = T(p[1]);  z = T(p[2]); }

		template <typename B>
		explicit TVec(const TVec<B,3> &v): x(T(v.x)), y(T(v.y)), z(T(v.z)) {}
#	endif

#	if I == 4
	// variables
		T	x,
			y,
			z,
			w;

	// constructors
		TVec(): x(0), y(0), z(0), w(0) {}
		TVec(const TVec<T,4> &v): x(v.x), y(v.y), z(v.z), w(v.w) {}

		explicit TVec(T V): x(V), y(V), z(V), w(V) {}
		explicit TVec(T X, T Y, T Z, T W): x(X), y(Y), z(Z), w(W) {}
		explicit TVec(const TVec<T,2> &XY): x(XY[0]), y(XY[1]), z(0), w(0) {}

		template <typename A, typename B>
		explicit TVec(A X, const TVec<T,2> &YZ, B W): x(X), y(YZ[0]), z(YZ[1]), w(W) {}

		template <typename A, typename B>
		explicit TVec(A X, B Y, const TVec<T,2> &ZW): x(X), y(Y), z(ZW[0]), w(ZW[1]) {}

		explicit TVec(const TVec<T,2> &XY, const TVec<T,2> &ZW): x(XY[0]), y(XY[1]), z(ZW[0]), w(ZW[1]) {}

		template <typename A>
		explicit TVec(const TVec<T,3> &XYZ, A W): x(XYZ[0]), y(XYZ[1]), z(XYZ[2]), w(W) {}

		explicit TVec(const TVec<T,3> &XYZ): x(XYZ[0]), y(XYZ[1]), z(XYZ[2]), w(0) {}

		template <typename B>
		explicit TVec(const B *p)	{ ASSUME( p != null );  x = T(p[0]);  y = T(p[1]);  z = T(p[2]);  w = T(p[3]); }

		template <typename B>
		explicit TVec(const TVec<B,4> &v): x(T(v.x)), y(T(v.y)), z(T(v.z)), w(T(v.w)) {}
#	endif

#	if I >= 5 or I < 0
		STATIC_WARNING( "not supported!" );
#	endif

		
		// unary operators
		VEC_UN_OPERATOR( - );
		VEC_UN_OPERATOR( ~ );
		VEC_UN_OPERATOR( ! );

		Self &		operator ++ ();
		Self &		operator -- ();
		const Self	operator ++ (int);
		const Self	operator -- (int);


		// binary operators
		T &			operator [] (usize i)						{ ASSUME(i<I);  return ptr()[i]; }
		T const &	operator [] (usize i) const					{ ASSUME(i<I);  return ptr()[i]; }

		bvec		operator == (const Self & right) const;
		bvec		operator != (const Self & right) const;
		bvec		operator >  (const Self & right) const;
		bvec		operator >= (const Self & right) const;
		bvec		operator <  (const Self & right) const;
		bvec		operator <= (const Self & right) const;

		VEC_BIN_OPERATOR( +  );
		VEC_BIN_OPERATOR( -  );
		VEC_BIN_OPERATOR( *  );
		VEC_BIN_OPERATOR( /  );
		VEC_BIN_OPERATOR( %  );
		VEC_BIN_OPERATOR( &  );
		VEC_BIN_OPERATOR( |  );
		VEC_BIN_OPERATOR( ^  );
		VEC_BIN_OPERATOR( << );
		VEC_BIN_OPERATOR( >> );
		
		VEC_BIN_OPERATOR2( && );
		VEC_BIN_OPERATOR2( || );


		// methods
		T *			ptr()										{ return PointerCast< T >( this ); }
		T const *	ptr()	const								{ return PointerCast< T >( this ); }
		arr_t &		ref()										{ return *PointerCast< arr_t >( this ); }
		arr_t const& ref()	const								{ return *PointerCast< arr_t >( this ); }

		static usize Count()									{ return I; }

		T			Min() const;
		T			Max() const;

		T			Length() const;
		T			LengthSqr() const;
		Self		Normalized() const;
		void		GetNormLength (OUT Self &norm, OUT T &length) const;
		Self &		Normalize();
		Self &		SetLength(T len);
		Self		NewLength(T len) const;

		// 
		Self &		MakeCeil(const Self &sVec);
		Self &		MakeFloor(const Self &sVec);

		T			Dot(const Self &right) const;
		T			DotAbs(const Self &right) const;

		T			Distance(const Self &sVec) const;
		T			DistanceSqr(const Self &sVec) const;

		Self &		InterpolateL(const Self &sVec1, const Self &sVec2, T tK);
		Self &		InterpolateQ(const Self &sVec1, const Self &sVec2, const Self &sVec3, T tK);

		Self		GetInterpolatedL(const Self &sVec, T tK) const;
		Self		GetInterpolatedQ(const Self &sVec1, const Self &sVec2, T tK) const;

		Self		Reflect (const Self &normal) const;
		Self		Refract (const Self &normal, T eta) const;

		template <typename IT>
		Self		Swizzle(const TVec<IT,I> &vSwizzle) const;

		template <typename T2>
		const TVec<T2,I> Convert() const;

		template <typename B>
		const B		To() const;

		//template <typename B, typename VT, usize C>
		//const B		To() const;

		string		ToString()		const;
		string		ToStringFull()	const;


		// compare methods
		/*
		bool		IsEqual(const Self &right)	const;	// ==
		bool		IsLess(const Self &right)	const;	// <
		bool		IsLEqual(const Self &right)	const;	// <=
		bool		IsGreater(const Self &right)const;	// >
		bool		IsGEqual(const Self &right)	const;	// >=
		//bool		IsZero()					const;
		//bool		IsNotZero()					const;*/

		bvec		Equal(const Self &right)	const;	// ==
		bvec		Less(const Self &right)		const;	// <
		bvec		LEqual(const Self &right)	const;	// <=
		bvec		Greater(const Self &right)	const;	// >
		bvec		GEqual(const Self &right)	const;	// >=
		bvec		IsZero()					const;
		bvec		IsNotZero()					const;


#	if I == 1
		T			Sum()		const	{ return x; }
		T			SumAbs()	const	{ return Abs(x); }
		T			Area()		const	{ return x*1; }
		T			Volume()	const	{ return x*1*1; }
#	endif

#	if I == 2
		T			Sum()		const	{ return x + y; }
		T			SumAbs()	const	{ return Abs(x) + Abs(y); }
		T			Volume()	const	{ return x*y*1; }
#	endif

#	if I >= 2
		T			Area()		const	{ return x*y; }
		TVec<T,2>	yx()		const	{ return TVec<T,2>( y, x ); }
		TVec<T,2>	xy()		const	{ return TVec<T,2>( x, y ); }
		TVec<T,2>	ox()		const	{ return TVec<T,2>( 0, x ); }
		TVec<T,2>	oy()		const	{ return TVec<T,2>( 0, y ); }
		
		template <usize _X, usize _Y>
		TVec<T,2>	Swizzle()	const	{ return TVec<T,2>( (*this)[_X], (*this)[_Y] ); }
#	endif

#	if I == 3
		T			Sum()		const	{ return x + y + z; }
		T			SumAbs()	const	{ return Abs(x) + Abs(y) + Abs(z); }
#	endif

#	if I >= 3
		T			Volume()	const	{ return x*y*z; }
		TVec<T,2>	yz()		const	{ return TVec<T,2>( y, z ); }
		TVec<T,2>	xz()		const	{ return TVec<T,2>( x, z ); }
		TVec<T,3>	xyz()		const	{ return TVec<T,3>( x, y, z ); }
		TVec<T,3>	zyx()		const	{ return TVec<T,3>( z, y, x ); }
		TVec<T,3>	xzy()		const	{ return TVec<T,3>( x, z, y ); }

		TVec<T,3>	xoo()		const	{ return TVec<T,3>( x, 0, 0 ); }
		TVec<T,3>	oyo()		const	{ return TVec<T,3>( 0, y, 0 ); }
		TVec<T,3>	ooz()		const	{ return TVec<T,3>( 0, 0, z ); }

		TVec<T,3>	xoz()		const	{ return TVec<T,3>( x, 0, z ); }
		TVec<T,3>	xyo()		const	{ return TVec<T,3>( x, y, 0 ); }
		TVec<T,3>	oyz()		const	{ return TVec<T,3>( 0, y, z ); }
		
		template <usize _X, usize _Y, usize _Z>
		TVec<T,3>	Swizzle()	const	{ return TVec<T,3>( (*this)[_X], (*this)[_Y], (*this)[_Z] ); }
#	endif

#	if I == 4
		T			Sum()		const	{ return x + y + z + w; }
		T			SumAbs()	const	{ return Abs(x) + Abs(y) + Abs(z) + Abs(w); }
#	endif

#	if I >= 4
		T			Volume4D()	const	{ return x*y*z*w; }
		TVec<T,2>	zw()		const	{ return TVec<T,2>( z, w ); }
		TVec<T,4>	xyzw()		const	{ return TVec<T,4>( x, y, z, w ); }
		TVec<T,4>	wzyx()		const	{ return TVec<T,4>( w, z, y, x ); }

		template <usize _X, usize _Y, usize _Z, usize _W>
		TVec<T,4>	Swizzle()	const	{ return TVec<T,4>( (*this)[_X], (*this)[_Y], (*this)[_Z], (*this)[_W] ); }
#	endif
	};



	
		
	template <typename T>
	inline TVec<T,I> &  TVec<T,I>::operator ++ ()
	{
		FOR( i, *this )	++(*this)[i];
		return *this;
	}

		
	template <typename T>
	inline TVec<T,I> &  TVec<T,I>::operator -- ()
	{
		FOR( i, *this )	--(*this)[i];
		return *this;
	}

		
	template <typename T>
	inline const TVec<T,I>  TVec<T,I>::operator ++ (int)
	{
		Self	ret(*this);
		++(*this);
		return ret;
	}

		
	template <typename T>
	inline const TVec<T,I>  TVec<T,I>::operator -- (int)
	{
		Self	ret(*this);
		--(*this);
		return ret;
	}

		
	template <typename T>
	inline TVec<bool,I>  TVec<T,I>::operator == (const Self & right) const
	{
		return Equal( right );
	}
	

	template <typename T>
	inline TVec<bool,I>  TVec<T,I>::operator != (const Self & right) const
	{
		return not Equal( right );
	}

		
	template <typename T>
	inline TVec<bool,I>  TVec<T,I>::operator >  (const Self & right) const
	{
		return Greater( right );
	}

		
	template <typename T>
	inline TVec<bool,I>  TVec<T,I>::operator >= (const Self & right) const
	{
		return GEqual( right );
	}

		
	template <typename T>
	inline TVec<bool,I>  TVec<T,I>::operator <  (const Self & right) const
	{
		return Less( right );
	}

		
	template <typename T>
	inline TVec<bool,I>  TVec<T,I>::operator <= (const Self & right) const
	{
		return LEqual( right );
	}

	
	template <typename T>
	inline T  TVec<T,I>::Min() const
	{
		T	t_min = (*this)[0];
		FOR( i, *this )  (*this)[i] < t_min ?  t_min = (*this)[i]  : 0;
		return t_min;
	}

	
	template <typename T>
	inline T  TVec<T,I>::Max() const
	{
		T	t_max = (*this)[0];
		FOR( i, *this )  (*this)[i] > t_max ?  t_max = (*this)[i]  : 0;
		return t_max;
	}

	
	template <typename T>
	inline T  TVec<T,I>::Length() const
	{
		return Sqrt( LengthSqr() );
	}

	
	template <typename T>
	inline T  TVec<T,I>::LengthSqr() const
	{
		Self	res;
		FOR( i, *this )	res[i] = UXMath::Square( (*this)[i] );
		return res.Sum();
	}

	
	template <typename T>
	inline TVec<T,I>  TVec<T,I>::Normalized() const
	{
		const T		len_sqr = LengthSqr();
		return len_sqr != T(0) ?  Self(*this) * InvSqrt( len_sqr )  :  Self();
	}
	
	
	template <typename T>
	inline void  TVec<T,I>::GetNormLength (OUT Self &norm, OUT T &length) const
	{
		length = LengthSqr();

		if ( UXMath::IsNotZero( length ) )
		{
			float l = InvSqrt( length );
			norm	= (*this) * l;
			length	= T(1) / l;
		}
		else
		{
			norm	= Self();
			length	= T(0);
		}
	}

		
	template <typename T>
	inline TVec<T,I> &  TVec<T,I>::Normalize()
	{
		return ( *this = Normalized() );
	}

		
	template <typename T>
	inline TVec<T,I> &  TVec<T,I>::SetLength(T len)
	{
		len *= InvSqrt( LengthSqr() );
		FOR( i, *this )  (*this)[i] *= len;
		return *this;
	}
	

	template <typename T>
	inline TVec<T,I>  TVec<T,I>::NewLength(T len) const
	{
		return Self( *this ).SetLength( len );
	}


	template <typename T>
	inline TVec<T,I> &  TVec<T,I>::MakeCeil(const Self &sVec)
	{
		FOR( i, *this )  sVec[i] > (*this)[i]  ?  (*this)[i] = sVec[i]  : 0;
		return *this;
	}
		

	template <typename T>
	inline TVec<T,I> &  TVec<T,I>::MakeFloor(const Self &sVec)
	{
		FOR( i, *this )  sVec[i] < (*this)[i]  ?  (*this)[i] = sVec[i]  : 0;
		return *this;
	}

		
	template <typename T>
	inline T  TVec<T,I>::Dot(const Self &right) const
	{
		Self	res;
		FOR( i, *this )	res[i] = (*this)[i] * right[i];
		return res.Sum();
	}
		

	template <typename T>
	inline T  TVec<T,I>::DotAbs(const Self &right) const
	{
		Self	res;
		FOR( i, *this )	res[i] = Abs( (*this)[i] * right[i] );
		return res.Sum();
	}

		
	template <typename T>
	inline T  TVec<T,I>::Distance(const Self &sVec) const
	{
		return ( (*this) - sVec ).Length();
	}
		

	template <typename T>
	inline T  TVec<T,I>::DistanceSqr(const Self &sVec) const
	{
		return ( (*this) - sVec ).LengthSqr();
	}

		
	template <typename T>
	inline TVec<T,I> &  TVec<T,I>::InterpolateL(const Self &sVec1, const Self &sVec2, T tK)
	{
		FOR( i, *this )	(*this)[i] = Mix( sVec1[i], sVec2[i], tK );
		return *this;
	}
		

	template <typename T>
	inline TVec<T,I> &  TVec<T,I>::InterpolateQ(const Self &sVec1, const Self &sVec2, const Self &sVec3, T tK)
	{
		FOR( i, *this )	(*this)[i] = QMix<T>( sVec1[i], sVec2[i], sVec3[i], tK );
		return *this;
	}

		
	template <typename T>
	inline TVec<T,I>  TVec<T,I>::GetInterpolatedL(const Self &sVec, T tK) const
	{
		Self	ret;
		FOR( i, *this )	ret[i] = Mix( (*this)[i], sVec[i], tK );
		return ret;
	}
		

	template <typename T>
	inline TVec<T,I>  TVec<T,I>::GetInterpolatedQ(const Self &sVec1, const Self &sVec2, T tK) const
	{
		Self	ret;
		FOR( i, *this )	ret[i] = QMix( (*this)[i], sVec1[i], sVec2[i], tK );
		return ret;
	}

		
	template <typename T>
	template <typename T2>
	inline const TVec<T2,I>  TVec<T,I>::Convert() const
	{
		TVec<T2,I>	ret;
		FOR( i, *this )	ret[i] = T2( (*this)[i] );
		return ret;
	}
	
		
	template <typename T>
	template <typename B>
	inline const B  TVec<T,I>::To() const
	{
		STATIC_ASSERT( typename B::_is_vector(true), "type is not vector" );
		
		B				ret;
		const usize		count = UXMath::Min( Count(), B::Count() );

		FOR_range( i, 0, count )	ret[i] = typename B::value_t( (*this)[i] );
		return ret;
	}
	
	/*
	template <typename T>
	template <typename B, typename VT, usize C>
	inline const B  TVec<T,I>::To() const
	{
		B				ret;
		const usize		count = UXMath::Min( Count(), C );

		FOR_range( i, 0, count )	ret[i] = VT( (*this)[i] );
		return ret;
	}
	*/
		
	template <typename T>
	inline string  TVec<T,I>::ToString() const
	{
		string	str;

		FOR( i, *this )		str << (*this)[i] << ", ";
		if ( str.Length() > 2 ) str.EraseFromBack( 2 );	// ", "

		return str;
	}
		

	template <typename T>
	inline string  TVec<T,I>::ToStringFull() const
	{
		static const char	s_aNames[] = { 'x', 'y', 'z', 'w' };

		string	str;
		FOR( i, *this )		str << s_aNames[i] << ": " << (*this)[i] << ", ";
		if ( str.Length() > 2 ) str.EraseFromBack( 2 );	// ", "

		return str;
	}
		

	template <typename T>
	inline TVec<bool,I>  TVec<T,I>::Equal(const Self &right) const
	{
		TVec<bool,I>	ret;
		FOR( i, *this )	ret[i] = UXMath::Equals( (*this)[i], right[i] );
		return ret;
	}
		

	template <typename T>
	inline TVec<bool,I>  TVec<T,I>::Less(const Self &right) const
	{
		TVec<bool,I>	ret;
		FOR( i, *this )	ret[i] = ( (*this)[i] < right[i] ) & not UXMath::Equals( (*this)[i], right[i] );
		return ret;
	}
		

	template <typename T>
	inline TVec<bool,I>  TVec<T,I>::LEqual(const Self &right) const
	{
		TVec<bool,I>	ret;
		FOR( i, *this )	ret[i] = ( (*this)[i] < right[i] ) | UXMath::Equals( (*this)[i], right[i] );
		return ret;
	}
		

	template <typename T>
	inline TVec<bool,I>  TVec<T,I>::Greater(const Self &right) const
	{
		TVec<bool,I>	ret;
		FOR( i, *this )	ret[i] = ( (*this)[i] > right[i] ) & not UXMath::Equals( (*this)[i], right[i] );
		return ret;
	}
		

	template <typename T>
	inline TVec<bool,I>  TVec<T,I>::GEqual(const Self &right) const
	{
		TVec<bool,I>	ret;
		FOR( i, *this )	ret[i] = ( (*this)[i] > right[i] ) | UXMath::Equals( (*this)[i], right[i] );
		return ret;
	}
		

	template <typename T>
	inline TVec<bool,I>  TVec<T,I>::IsZero() const
	{
		TVec<bool,I>	ret;
		FOR( i, *this )	ret[i] = UXMath::IsZero( (*this)[i] );
		return ret;
	}
		

	template <typename T>
	inline TVec<bool,I>  TVec<T,I>::IsNotZero() const
	{
		TVec<bool,I>	ret;
		FOR( i, *this )	ret[i] = UXMath::IsNotZero( (*this)[i] );
		return ret;
	}
	
	/*
	template <typename T>
	inline bool  TVec<T,I>::IsEqual(const Self &right) const
	{
		return All( Equal( right ) );
	}

	
	template <typename T>
	inline bool  TVec<T,I>::IsLess(const Self &right) const
	{
		if ( All( Less( right ) ) )
			return true;

		return 
	}

	
	template <typename T>
	inline bool  TVec<T,I>::IsLEqual(const Self &right) const
	{
	}

	
	template <typename T>
	inline bool  TVec<T,I>::IsGreater(const Self &right) const
	{
	}

	
	template <typename T>
	inline bool  TVec<T,I>::IsGEqual(const Self &right) const
	{
	}
	*/

	template <typename T>
	template <typename IT>
	inline TVec<T,I>  TVec<T,I>::Swizzle(const TVec<IT,I> &vSwizzle) const
	{
		TypeDescriptor::MustBeInteger<IT>();

		Self	ret;
		FOR( i, *this )	ret[i] = (*this)[ vSwizzle[i] & 3 ];
		return ret;
	}
	
		
	template <typename T>
	inline TVec<T,I>  TVec<T,I>::Reflect (const Self &normal) const
	{
		return (*this) - T(2) * normal.Dot( *this ) * normal;
	}

		
	template <typename T>
	inline TVec<T,I>  TVec<T,I>::Refract (const Self &normal, T eta) const
	{
		T const		k = T(1) - eta * eta * ( T(1) - UXMath::Square( normal.Dot( *this ) ) );
		Self		r;

		if ( k >= T(0) )
			r = eta * (*this) - ( eta * normal.Dot( *this ) + Sqrt( k ) ) * normal;

		return r;
	}
